package cn.zyjblogs.server.client.service.impl;

import cn.zyjblogs.server.client.mapper.OauthClientDetailsMapper;
import cn.zyjblogs.server.client.po.OauthClientDetail;
import cn.zyjblogs.starter.common.utils.bean.BeanUtils;
import cn.zyjblogs.starter.redis.utils.RedisTemplateHandler;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zhuyijun
 */
@Service("oauthClientDetailsService")
@RequiredArgsConstructor
public class OauthClientDetailsServiceImpl implements ClientDetailsService, ClientRegistrationService {
    private final OauthClientDetailsMapper oauthClientDetailsMapper;
    private final PasswordEncoder passwordEncoder;
    private final RedisTemplateHandler<String, BaseClientDetails> redisTemplateHandler;
    private static final String CLIENT = "OAUTH:CLIENT";

    @Override
    public OauthClientDetail loadClientByClientId(String clientId) throws ClientRegistrationException {
        OauthClientDetail baseClientDetails1 = redisTemplateHandler.<String, OauthClientDetail>hGet(CLIENT, clientId);
        if (baseClientDetails1 != null) {
            return baseClientDetails1;
        }
        LambdaQueryWrapper<OauthClientDetail> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(OauthClientDetail::getClientId, clientId);
        OauthClientDetail baseClientDetails = oauthClientDetailsMapper.selectOne(wrapper);
        if (baseClientDetails != null) {
            redisTemplateHandler.hPut(CLIENT, clientId, baseClientDetails);
        }
        return baseClientDetails;
    }

    @Override
    public void addClientDetails(ClientDetails clientDetails) throws ClientAlreadyExistsException {
        OauthClientDetail oauthClientDetail = new OauthClientDetail(clientDetails);
        if (oauthClientDetail.getClientSecret() != null) {
            oauthClientDetail.setClientSecret(passwordEncoder.encode(oauthClientDetail.getClientSecret()));
        }
        int insert = oauthClientDetailsMapper.insert(oauthClientDetail);
        if (insert > 0) {
            redisTemplateHandler.hPut(CLIENT, clientDetails.getClientId(), clientDetails);
        }
    }

    @Override
    public void updateClientDetails(ClientDetails clientDetails) throws NoSuchClientException {
        LambdaUpdateWrapper<OauthClientDetail> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(OauthClientDetail::getClientId, clientDetails.getClientId());
        OauthClientDetail oauthClientDetail = new OauthClientDetail(clientDetails);
        oauthClientDetailsMapper.update(oauthClientDetail, updateWrapper);
        redisTemplateHandler.hDelete(CLIENT, clientDetails.getClientId());
    }

    @Override
    public void updateClientSecret(String clientId, String clientSecret) throws NoSuchClientException {
        LambdaUpdateWrapper<OauthClientDetail> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(OauthClientDetail::getClientId, clientId).set(OauthClientDetail::getClientSecret, passwordEncoder.encode(clientSecret));
        oauthClientDetailsMapper.update(null, updateWrapper);
        redisTemplateHandler.hDelete(CLIENT, clientId);
    }

    @Override
    public void removeClientDetails(String clientId) throws NoSuchClientException {
        LambdaUpdateWrapper<OauthClientDetail> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(OauthClientDetail::getClientId, clientId);
        oauthClientDetailsMapper.delete(updateWrapper);
        redisTemplateHandler.hDelete(CLIENT, clientId);
    }

    @Override
    public List<ClientDetails> listClientDetails() {
        List<OauthClientDetail> oauthClientDetails = oauthClientDetailsMapper.selectList(Wrappers.emptyWrapper());
        if (CollectionUtils.isEmpty(oauthClientDetails)) {
            return new ArrayList<>();
        }
        return BeanUtils.map(oauthClientDetails, ClientDetails.class);
    }
}
